#include "simple/support/type_traits.hpp"

using namespace simple::support;

void Conversions()
{
	static_assert(converts_to<bool, int>);
	static_assert(converts_to<int, bool>);
	static_assert(not converts_to<int*, int>);
	static_assert(imp_converts_to<int, bool>);
	static_assert(imp_converts_to<bool, int>);
	static_assert(not exp_converts_to<bool, int>);
	static_assert(not exp_converts_to<int, bool>);
	struct base {};
	struct derived : base {};
	static_assert(exp_converts_to<base, derived>);
	static_assert(not imp_converts_to<base, derived>);
	static_assert(converts_to<base, derived>);
	static_assert(not exp_converts_to<derived, base>);
	static_assert(imp_converts_to<derived, base>);
	static_assert(converts_to<derived, base>);
}

void CanDeref()
{
	static_assert(can_deref_v<int*>);
	static_assert(can_deref_v<const int*>);
	static_assert(not can_deref_v<int>);
	static_assert(not can_deref_v<const int>);
}

struct ntl {};

struct tl {};
template <> struct std::tuple_size<tl> { constexpr static auto value = 2; };
template <std::size_t i> struct std::tuple_element<i,tl> { using type = int; };
template <std::size_t i> auto get(tl) { return int{}; }

void HasTupleInterface()
{
	static_assert(has_tuple_interface_v<std::tuple<int>>);
	static_assert(not has_tuple_interface_v<int>);
	static_assert(not has_tuple_interface_v<ntl>);
	static_assert(has_tuple_interface_v<tl>);
}

void IsMetaConstant()
{
	static_assert(not is_meta_constant_v<int>);
	static_assert(is_meta_constant_v<is_meta_constant<int>>);
	static_assert(is_meta_constant_v<std::is_same<int,int>>);
	static_assert(is_meta_constant_v<std::is_same<int,bool>>);
	static_assert(is_meta_constant_v<std::is_integral<int>>);
	static_assert(is_meta_constant_v<std::is_integral<int*>>);
	static_assert(is_meta_constant_v<can_deref<int*>>);
	static_assert(not is_meta_constant_v<std::make_unsigned<int>>);
	static_assert(is_meta_constant_v<std::integral_constant<int,0>>);
	static_assert(not is_meta_constant_v<std::tuple_size<tl>>); // even though tl is tuple like and can be structurally bound ??? wild wild west...
	[[maybe_unused]] auto [x,y] = tl{}; // see?
	static_assert(is_meta_constant_v<std::tuple_size<std::tuple<int,int>>>); // now i'm not sure if this is required or just happens to be...
	static_assert(not is_meta_constant_v<std::tuple_size<int>>);
	static_assert(not is_meta_constant_v<std::tuple_element<1,tl>>);
}

void IsTemplateInstance()
{
	static_assert(is_template_instance_v<std::tuple, std::tuple<int,int>>);
	static_assert(not is_template_instance_v<std::tuple, std::pair<int,int>>);
}

int main()
{
	Conversions();
	CanDeref();
	HasTupleInterface();
	IsMetaConstant();
	IsTemplateInstance();
	return 0;
}
